/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import i18n from 'i18next';

import {
  CommandDialog,
  ICmdDialogParams,
  ParameterObjectType,
  pickedFaceData,
  pickedLineData,
  pickedVertexData,
  RawMeasureResult,
} from '@amcax/base';
import { CurvedBaseCommand } from '@app-cad/types';
import { useEffect, useMemo, useRef, useState } from 'react';
import { wsApi } from '@app-cad/server';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { CommandFormWrapper } from '@app-base/common/components/command-form';
import { CommandFormFooter } from '@app-base/common/components/command-form/footer';
import { PickInputForFocus } from '@app-base/common/components/command-form/auto-focus/pick-input';
import { PickInputValueType } from '@app-base/common/components/pick-input/types';
import { getIdOfPickedObj } from '@app-base/common/utils/render';
import { MeasureResult } from '@app-base/common/utils/measure/types';
import { ctx2, docMgr } from '@app-base/module';
import {
  getCSSColorByNumber,
  parseMeasureResult,
  useMeasureText,
} from '@app-base/common/utils/measure';
import { AngleLabel } from '@app-base/common/utils/measure/Angle';
import {
  DistanceLineType,
  MEASURE_DISTANCE_COLORS,
} from '@app-base/common/utils/measure/DistanceLine';
import { CaretDownOutlined, CaretRightOutlined } from '@ant-design/icons';
import { measureRenderManager } from '@app-base/manager/MeasureRenderManager';
import { MeasureObject } from '@app-base/common/utils/measure/renderObject';
import { isEqual } from 'lodash';

type RenderValueObj = {
  value: number;
  label: string;
  type: ParameterObjectType | 'Area';
  labelColor?: string;
  children?: RenderValueObj[];
};

const RenderValues = ({
  value,
  label,
  type,
  labelColor,
  children,
}: RenderValueObj) => {
  const text = useMeasureText(value, type);

  const [isExpand, setIsExpand] = useState(false);

  const ExpandIcon = isExpand ? CaretDownOutlined : CaretRightOutlined;

  return (
    <div className='mt-2 text-sm'>
      <div className='flex justify-between'>
        <div className='flex flex-shrink-0'>
          {labelColor && (
            <div
              className='w-1 h-full mr-1'
              style={{
                backgroundColor: labelColor,
              }}
            />
          )}
          {label}
          {children?.length > 0 && (
            <ExpandIcon
              className='cursor-pointer ml-1'
              onClick={() => {
                setIsExpand((old) => !old);
              }}
            />
          )}
        </div>
        <div className='flex-1 ml-2 text-right whitespace-nowrap'>{text}</div>
      </div>
      {children && isExpand && (
        <div className='ml-4'>
          {children?.map((v) => <RenderValues {...v} />)}
        </div>
      )}
    </div>
  );
};

function MeasureCommand({ title, onClose }: ICmdDialogParams) {
  const [isLoading, setIsLoading] = useState(false);
  const [measureTargets, setMeasureTargets] = useState<
    (pickedFaceData | pickedLineData | pickedVertexData)[]
  >([]);

  const [measureResult, setMeasureResult] = useState<MeasureResult>();
  const previewResult = useRef<MeasureObject>();

  const clearPreview = () => {
    previewResult.current?.destroy();
    previewResult.current = null;
    ctx2.viewer.requestRender();
  };

  useEffect(() => {
    return clearPreview;
  }, []);

  const execute = async (preview: boolean) => {
    const oldIds = [measureResult?.dxid1, measureResult?.dxid2];
    const oldPositions = previewResult.current?.getLabelPositions();
    clearPreview();

    if (!isValid) {
      setMeasureResult(null);
      return;
    }

    const result: RawMeasureResult = await wsApi.measure(
      getIdOfPickedObj(measureTargets[0]),
      getIdOfPickedObj(measureTargets[1]),
      true,
    );

    if (destroyedFlag.current) {
      return;
    }

    if (!preview) {
      const { id } = await wsApi.addMeasure(result);
      if (isEqual(oldIds, [result.dxid1, result.dxid2])) {
        if (id != null && oldPositions != null) {
          measureRenderManager.labelPositions.set(id, oldPositions);
        }
      }

      return;
    }

    const parsedResult = parseMeasureResult(result);

    setMeasureResult(parsedResult);

    previewResult.current = new MeasureObject(parsedResult);
    await previewResult.current.render();

    ctx2.cadScene.assisGroup.add(previewResult.current);

    ctx2.viewer.requestRender();
  };

  useEffect(() => {
    execute(true);
  }, [measureTargets]);

  const isValid = useMemo(() => {
    return measureTargets.length > 0;
  }, [measureTargets]);

  const onSubmit = async () => {
    setIsLoading(true);
    try {
      await execute(false);
      await docMgr.updateDocumentObjects();
      await measureRenderManager.rerenderMeasureResults();
    } finally {
      setIsLoading(false);
    }

    onClose();
  };

  const { destroyedFlag } = useCommand('measure', {
    onClose,
    finishDraw: onSubmit,
  });

  const resultForRender = useMemo(() => {
    if (measureResult == null) {
      return null;
    }

    const result: RenderValueObj[] = [];

    if (measureResult.angle != null) {
      result.push({
        label: '角度',
        value: measureResult.angle.value,
        type: ParameterObjectType.Angle,
        labelColor: getCSSColorByNumber(AngleLabel.color),
      });
    }

    if (measureResult.area != null) {
      result.push({
        label: '面积',
        value: measureResult.area.value,
        type: 'Area',
      });
    }

    if (measureResult.centerDistance != null) {
      result.push({
        label: '中心',
        value: measureResult.centerDistance.value,
        type: ParameterObjectType.Length,
        labelColor: getCSSColorByNumber(
          MEASURE_DISTANCE_COLORS[DistanceLineType.Full],
        ),
        children: [
          {
            label: 'X中心',
            value: measureResult.centerDistance.xValue,
            type: ParameterObjectType.Length,
            labelColor: getCSSColorByNumber(
              MEASURE_DISTANCE_COLORS[DistanceLineType.X],
            ),
          },
          {
            label: 'Y中心',
            value: measureResult.centerDistance.yValue,
            type: ParameterObjectType.Length,
            labelColor: getCSSColorByNumber(
              MEASURE_DISTANCE_COLORS[DistanceLineType.Y],
            ),
          },
          {
            label: 'Z中心',
            value: measureResult.centerDistance.zValue,
            type: ParameterObjectType.Length,
            labelColor: getCSSColorByNumber(
              MEASURE_DISTANCE_COLORS[DistanceLineType.Z],
            ),
          },
        ],
      });
    }

    if (measureResult.minDistance != null) {
      result.push({
        label: '最小值',
        value: measureResult.minDistance.value,
        type: ParameterObjectType.Length,
        labelColor: getCSSColorByNumber(
          MEASURE_DISTANCE_COLORS[DistanceLineType.Full],
        ),
        children: [
          {
            label: 'X最小值',
            value: measureResult.minDistance.xValue,
            type: ParameterObjectType.Length,
            labelColor: getCSSColorByNumber(
              MEASURE_DISTANCE_COLORS[DistanceLineType.X],
            ),
          },
          {
            label: 'Y最小值',
            value: measureResult.minDistance.yValue,
            type: ParameterObjectType.Length,
            labelColor: getCSSColorByNumber(
              MEASURE_DISTANCE_COLORS[DistanceLineType.Y],
            ),
          },
          {
            label: 'Z最小值',
            value: measureResult.minDistance.zValue,
            type: ParameterObjectType.Length,
            labelColor: getCSSColorByNumber(
              MEASURE_DISTANCE_COLORS[DistanceLineType.Z],
            ),
          },
        ].filter(({ value }) => value > 1e-12),
      });
    }

    if (measureResult.length != null) {
      result.push({
        label: '长度',
        value: measureResult.length.value,
        type: ParameterObjectType.Length,
      });
    }

    if (measureResult.coordinate != null) {
      result.push(
        ...[
          {
            label: 'X坐标',
            value: measureResult.coordinate[0],
            type: ParameterObjectType.Length,
          },
          {
            label: 'Y坐标',
            value: measureResult.coordinate[1],
            type: ParameterObjectType.Length,
          },
          {
            label: 'Z坐标',
            value: measureResult.coordinate[2],
            type: ParameterObjectType.Length,
          },
        ].filter(({ value }) => value > 1e-12),
      );
    }

    if (measureResult.radius != null) {
      result.push({
        label: '半径',
        value: measureResult.radius.value,
        type: ParameterObjectType.Length,
      });
    }

    if (measureResult.round != null) {
      result.push({
        label: '周长',
        value: measureResult.round.value,
        type: ParameterObjectType.Length,
      });
    }

    return result;
  }, [measureResult]);

  return (
    <CommandDialog
      onClose={onClose}
      title={title}
      placement='topLeft'
    >
      <CommandFormWrapper>
        <PickInputForFocus
          autoFocus
          placeholder='请选择测量对象'
          alwaysPreserverSuffix
          allowPickType={[
            PickInputValueType.Vertex,
            PickInputValueType.Curve,
            PickInputValueType.Surface,
          ]}
          allowInfinitePlane
          value={measureTargets}
          onPick={(result) => {
            if (result.length > 2) {
              return;
            }
            if (result.some((r) => r.idInfo.o === 'ORIGIN')) {
              return;
            }
            setMeasureTargets(result);
          }}
          allowMarqueeSelect={false}
          isMulti
        />
        {resultForRender?.map((v, index) => (
          <RenderValues
            key={index}
            {...v}
          />
        ))}
        <CommandFormFooter
          isSubmitLoading={isLoading}
          onClose={onClose}
          onSubmit={onSubmit}
          submitText='保存'
          isSubmitDisabled={!isValid}
        />
      </CommandFormWrapper>
    </CommandDialog>
  );
}

export const cmdMeasure: CurvedBaseCommand = {
  id: 'CmdMeasure',
  label: 'command.measure',
  icon: 'commands/feature/measure',
  create: (label, onClose) => (
    <MeasureCommand
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
